// @ts-check
const { URL } = require('url')
const http = require('http')
const https = require('https')
/**
 *
 * @param {import('f2e-server').F2EConfig} conf
 * @param {{
 * setBefore?: number,
 * test: RegExp,
 * origin: string | string[],
 * debug?: boolean,
 * timeout?: number,
 * pathname?: string | {(s:string, ...args: any[]): string},
 * renderHeaders?: (req: http.IncomingMessage) => http.IncomingHttpHeaders
 * responseHeaders?: (resp: http.IncomingMessage, req: http.IncomingMessage) => http.OutgoingHttpHeaders
 * responseRender?: (buffer: Buffer) => string
 * options?: http.RequestOptions
 * }} _options
 * @returns
 */
module.exports = (conf, _options) => {
    const {
        setBefore = 0,
        /** 代理origin */
        origin,
        /** 代理pathname，用于req.url.replace */
        pathname = m => m,
        debug,
        timeout = 120000,
        renderHeaders = req => req.headers,
        responseHeaders = (resp, req) => resp.headers,
        responseRender,
        options = {},
        test = /.*/
    } = _options

    if (!origin) {
        throw new Error('origin needed!')
    }
    const origin_provider = (function (origins) {
        let i = 0
        return {
            get: () => {
                i = (i + 1) % origins.length
                return origins[i]
            }
        }
    })([''].concat(origin).filter(l => !!l));

    /**
     *
     * @param {string} path
     * @param {import('http').IncomingMessage} req
     * @param {import('http').ServerResponse} resp
     */
    const render = function (path, req, resp) {
        if (typeof req.url != 'undefined' && test.test(req.url)) {
            const _origin = origin_provider.get()
            try {
                // @ts-ignore
                const newPath = new URL(req.url && req.url.replace(test, pathname) || '/', _origin)
                debug && console.log(newPath, 'begin')
                const creq = (/^https/i.test(newPath.protocol) ? https : http).request(newPath, {
                    method: req.method,
                    headers: Object.assign({}, {
                        host: _origin.replace(/^https?:\/\/([^\/]+).*$/, '$1')
                    }, renderHeaders(req)),
                    timeout,
                    ...options,
                }, function (res) {
                    debug && console.log(newPath, 'response')
                    if (responseRender) {
                        const chunks = []
                        res.on('data', function (data) {
                            chunks.push(data)
                        })
                        res.on('end', function () {
                            resp.end(responseRender(Buffer.concat(chunks)))
                        })
                    } else {
                        res.pipe(resp)
                    }
                }).on('response', function (response) {
                    resp.writeHead(response.statusCode || 200, response.statusMessage, responseHeaders(response, req))
                }).on('timeout', function () {
                    debug && console.log(newPath, 'timeout')
                }).on('error', function (err) {
                    debug && console.log(newPath, 'error')
                    resp.writeHead(500)
                    resp.end(err.toString())
                });
                req.pipe(creq)
            } catch (e) {
                resp.writeHead(500)
                resp.end(e.toString())
            }
            return false
        }
    }

    return {
        setBefore,
        beforeRoute: render
    }
}
